package com.specter.boot.auto;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.springframework.aop.Advisor;
import org.springframework.aop.aspectj.AspectJExpressionPointcut;
import org.springframework.aop.support.DefaultPointcutAdvisor;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Role;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.interceptor.NameMatchTransactionAttributeSource;
import org.springframework.transaction.interceptor.RollbackRuleAttribute;
import org.springframework.transaction.interceptor.RuleBasedTransactionAttribute;
import org.springframework.transaction.interceptor.TransactionAttribute;
import org.springframework.transaction.interceptor.TransactionInterceptor;

import com.specter.mvc.model.batis.MybatisSqlSessionFactoryBean;
import com.specter.mvc.web.spring.ConventionRequestMappingHandlerMapping;

@Configuration
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public class SpecterMvcStarterConfiguration {

    /**
     * TransactionInterceptor配置
     * 
     * @param transactionManager 事务管理器
     * @return 事务拦截器
     */
    @Bean
    public TransactionInterceptor transactionInterceptor(PlatformTransactionManager transactionManager) {
        NameMatchTransactionAttributeSource source = new NameMatchTransactionAttributeSource();
        Map<String, TransactionAttribute> nameMap = new HashMap<>();

        RuleBasedTransactionAttribute readOnlyTx = new RuleBasedTransactionAttribute();
        readOnlyTx.setReadOnly(true);
        readOnlyTx.setPropagationBehavior(TransactionDefinition.PROPAGATION_SUPPORTS);

        RuleBasedTransactionAttribute requiredTx = new RuleBasedTransactionAttribute();
        requiredTx.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);

        RuleBasedTransactionAttribute rollbackTx = new RuleBasedTransactionAttribute();
        rollbackTx.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
        List<RollbackRuleAttribute> rollbackRules = new ArrayList<>();
        rollbackRules.add(new RollbackRuleAttribute(Exception.class));
        rollbackRules.add(new RollbackRuleAttribute(RuntimeException.class));
        rollbackTx.setRollbackRules(rollbackRules);

        nameMap.put("find*", readOnlyTx);
        nameMap.put("select*", readOnlyTx);
        nameMap.put("insert*", requiredTx);
        nameMap.put("update*", requiredTx);
        nameMap.put("delete*", requiredTx);
        nameMap.put("*", rollbackTx);

        source.setNameMap(nameMap);
        TransactionInterceptor interceptor = new TransactionInterceptor();
        interceptor.setTransactionManager(transactionManager);
        interceptor.setTransactionAttributeSource(source);
        return interceptor;
    }

    /**
     * 统一事务Advisor切面配置
     * 
     * @param transactionInterceptor 事务拦截器
     * @return Advisor切面
     */
    @Bean
    @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
    public Advisor transactionAdvisor(TransactionInterceptor transactionInterceptor) {
        AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
        pointcut.setExpression("execution(* *..impl.*ServiceImpl.*(..))");
        return new DefaultPointcutAdvisor(pointcut, transactionInterceptor);
    }

    @Bean
    @ConfigurationProperties(prefix = "specter.mvc.model.batis")
    public MybatisSqlSessionFactoryBean sqlSessionFactory() {
        return new MybatisSqlSessionFactoryBean();
    }

    @Bean
    @ConfigurationProperties(prefix = "specter.mvc.web.spring")
    public ConventionRequestMappingHandlerMapping requestMappingHandlerMapping() {
        return new ConventionRequestMappingHandlerMapping();
    }
}